package com.xs.gis.cover.coverImpl;

import com.xs.gis.cover.FileCover;
import com.xs.gis.domain.TxtPoint;
import com.xs.gis.domain.geojson.*;
import com.xs.gis.util.CRSUtils;
import com.xs.gis.util.JacksonUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.geotools.geojson.geom.GeometryJSON;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.json.simple.JSONObject;
import org.locationtech.jts.geom.*;

import java.io.*;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**

 * @description: 格式化工具
 * @author: w
 * @create: 2023-08-15 10:15
 **/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class GisTxtCover implements FileCover {


    private static final String SPLIT_HEAD = "[属性描述]";
    private static final String SPLIT_PLOT = "[地块坐标]";
    /**
     * 字符转 需要有换行符号
     */

    private String text;
    private String charsetName="UTF-8";


    public GisTxtCover(String text) {
        this.text = text;
    }

    @Override
    public String featureToFile(GeoJsonFeature feature, String filePath) throws IOException {
        List<GeoFeature> features = feature.getFeatures();
        GeoFeature geoFeature = features.get(0);
        Geometry geometry0 = geoFeature.formatGeometry();
        String x = String.valueOf(new BigDecimal(geometry0.getCoordinate().getX()));
//        获取带号
        String tapeNumber = x.substring(0, 2);

        FileWriter fileWriter = new FileWriter(filePath);
        BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
        bufferedWriter.write(SPLIT_HEAD);
        bufferedWriter.newLine();
        setHead(bufferedWriter,geometry0,tapeNumber);
        bufferedWriter.write(SPLIT_PLOT);
        bufferedWriter.newLine();

        for (GeoFeature geoFeatureItem : features) {
            Geometry geometry = geoFeatureItem.formatGeometry();
            bufferedWriter = writeToStream(bufferedWriter,geometry);
        }

        bufferedWriter.flush();
        fileWriter.flush();
        bufferedWriter.close();
        fileWriter.close();
        return filePath;
    }

    @Override
    public GeoJsonFeature fileToFeature(String filePath) throws IOException {

        String context = text;
        if (StringUtils.isNotBlank(filePath)) {
            BufferedReader reader =new BufferedReader(new InputStreamReader(new FileInputStream(filePath),charsetName));

            StringBuffer fileContent = new StringBuffer();
            // 逐行读取文件内容
            String line;
            while ((line = reader.readLine()) != null) {
                fileContent.append(line);
                fileContent.append("\n");
            }
            // 关闭 BufferedReader
            reader.close();
            context = fileContent.toString();
        }
        GeoJsonFeature geoJsonFeature = parseTextToJson(context);
        text=null;
        return geoJsonFeature;
    }


    /**
     * 字符转json
     *
     * @param fileContent
     * @return
     */
    private GeoJsonFeature parseTextToJson(String fileContent) {
        String[] split = fileContent.split("\n");
        List<String> list = Arrays.asList(split);

        boolean isHead = true;

        JSONObject localProperty = null;

        List<TxtPoint> txtPoints = new ArrayList<>();
        List<GeoFeature> features = new ArrayList<>();
        String srs = "";
        String utm = "";
        Pattern pattern = Pattern.compile("^\\d+");

        for (int i = 0; i < list.size(); i++) {
            String readLine = list.get(i);

            if (readLine.contains(SPLIT_PLOT)) {
                isHead = false;
            }
            //解析带号
            if (isHead) {
                String[] pointStringArr = readLine.split("=");
                if (pointStringArr[0] .contains( "带号")) {
                    Matcher matcher = pattern.matcher(pointStringArr[1]);
                    if (matcher.find()){
                        utm = matcher.group();
                    }
                }
                if (pointStringArr[0].contains("坐标系")) {
                    srs = pointStringArr[1];
                }
            }

            if (!isHead) {
                //一个面
                String[] pointStringArr = readLine.split(",");
                if (readLine.endsWith("@")) {
                    //构建一个面数据
                    if (localProperty != null && CollectionUtils.isNotEmpty(txtPoints)) {
                        GeoFeature geoFeature = buildFeature(localProperty, txtPoints);
                        features.add(geoFeature);
                    }
                    //继续解析下一个
                    txtPoints = new ArrayList<>();
                    //构建属性
                    localProperty = new JSONObject();
                    localProperty.put("界址点数", pointStringArr[0]);
                    localProperty.put("地块面积", pointStringArr[1]);
                    localProperty.put("地块编号", pointStringArr[2]);
                    localProperty.put("地块名称", pointStringArr[3]);
                    localProperty.put("几何类型", pointStringArr[4]);
                    localProperty.put("图幅号", pointStringArr[5]);
                    localProperty.put("地块用途", pointStringArr[6]);
                }
                //解析面
                if (readLine.startsWith("J")) {

                    String x=pointStringArr[3].startsWith(utm)?pointStringArr[3]:pointStringArr[2];
                    String y=pointStringArr[2].startsWith(utm)?pointStringArr[3]:pointStringArr[2];


                    TxtPoint textPoint = TxtPoint.builder()
                            .pointIndex(Integer.parseInt(pointStringArr[0].replace("J", "")))
                            .polygonIndex(Integer.parseInt(pointStringArr[1]))
                            .x(new BigDecimal(x))
                            .y(new BigDecimal(y))
                            .build();
                    txtPoints.add(textPoint);
                }
            }
        }

        //再构建最后一个
        GeoFeature geoFeature = buildFeature(localProperty, txtPoints);
        features.add(geoFeature);
        GeoJsonFeature geoJsonFeature = new GeoJsonFeature();
        if (srs.contains("2000")) {
            String epsgCode = CRSUtils.getCRSByUTM(CRSUtils.CGCS2000, utm);
            GeoCrs geoCrs = new GeoCrs();
            GeoCrsProperties geoCrsProperties = new GeoCrsProperties();
            geoCrsProperties.setName(epsgCode);
            geoCrs.setType("name");
            geoCrs.setProperties(geoCrsProperties);
            geoJsonFeature.setCrs(geoCrs);
        }
        geoJsonFeature.setFeatures(features);

        return geoJsonFeature;
    }

    private GeoFeature buildFeature(JSONObject localProperty, List<TxtPoint> txtPoints) {
        GeoFeature geoFeature = new GeoFeature();
        geoFeature.setProperties(localProperty);
        Geometry geometry = parserTxtPoint(txtPoints);
        GeometryJSON g = new GeometryJSON(15);
        String geoJSON = g.toString(geometry);
        geoFeature.setGeometry(JacksonUtil.toObject(geoJSON, BaseGeometry.class));
        geoFeature.setType("Feature");
        return geoFeature;
    }

    private Geometry parserTxtPoint(List<TxtPoint> txtPoints) {

        GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();

        Map<Integer, List<TxtPoint>> pointMap = txtPoints.stream().collect(Collectors.groupingBy(TxtPoint::getPolygonIndex));
        List<Polygon> polygons = new ArrayList<>();
        for (Integer index : pointMap.keySet()) {
            List<TxtPoint> subTxtPoints = pointMap.get(index);
            List<Coordinate> pointList = subTxtPoints.stream().map(x ->
                    new Coordinate(x.getX().doubleValue(), x.getY().doubleValue())
            ).collect(Collectors.toList());
            Polygon polygon = geometryFactory.createPolygon(pointList.toArray(new Coordinate[pointList.size()]));
            polygons.add(polygon);
        }
        if (polygons.size() > 1) {
            return geometryFactory.createMultiPolygon(polygons.toArray(new Polygon[0]));
        } else {
            return polygons.get(0);
        }


    }

//
////    /***
////     *  自定义头
////     * @param filePath 文件路径
////     * @param geometry
////     * @param head 头内容
////     */
//    public void geometryToTxt(String filePath, Geometry geometry, String head) {
//
//        try {
//            FileWriter fileWriter = new FileWriter(filePath);
//            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
//            BufferedWriter bufferedWriterRe = writeToStream(bufferedWriter, geometry, head);
//            bufferedWriterRe.flush();
//            fileWriter.flush();
//            bufferedWriterRe.close();
//            fileWriter.close();
//        } catch (IOException e) {
//            throw new RuntimeException(e);
//        }
//    }

    /***
     *写入到stream
     * @param geometry
     * @return
     * @throws IOException
     */
    private BufferedWriter writeToStream(BufferedWriter bufferedWriter, Geometry geometry) throws IOException {
        //头
//
//        bufferedWriter.write(SPLIT_HEAD);
//        bufferedWriter.newLine();
//        setHead(bufferedWriter,geometry);
        //
//        bufferedWriter.write(SPLIT_PLOT);
//        bufferedWriter.newLine();

        if (geometry instanceof Point) {
            //TODO
        }
        //线
        if (geometry instanceof LineString) {
            LineString lineString = (LineString) geometry;
            List<TxtPoint> txtPoints = writePoint(lineString, 1, bufferedWriter);
            bufferedWriter.write(String.format("%s,%s,%s,%s,线,%s,%s,%s,@", txtPoints.size(), geometry.getArea(), "", "", "", "", ""));
            bufferedWriter.newLine();
            for (TxtPoint txtPoint : txtPoints) {
                bufferedWriter.write(String.format("J%s,%s,%s,%s", txtPoint.getPointIndex(), txtPoint.getPolygonIndex(), txtPoint.getX(), txtPoint.getY()));
                bufferedWriter.newLine();
            }
        }
        //面
        if (geometry instanceof Polygon) {
            Polygon polygon = (Polygon) geometry;
            List<TxtPoint> txtPoints = writePoint(polygon, 1, bufferedWriter);
            bufferedWriter.write(String.format("%s,%s,%s,%s,面,%s,%s,%s,@", txtPoints.size(), geometry.getArea(), "", "", "", "", ""));
            bufferedWriter.newLine();
            for (TxtPoint txtPoint : txtPoints) {
                bufferedWriter.write(String.format("J%s,%s,%s,%s", txtPoint.getPointIndex(), txtPoint.getPolygonIndex(), txtPoint.getX(), txtPoint.getY()));
                bufferedWriter.newLine();
            }
        }
        //多面
        if (geometry instanceof MultiPolygon) {
            MultiPolygon multiPolygon = (MultiPolygon) geometry;
            int numGeometries = multiPolygon.getNumGeometries();

            List<TxtPoint> txtPointAll = new ArrayList();
            for (int i = 0; i < numGeometries; i++) {
                Polygon geometryN = (Polygon) multiPolygon.getGeometryN(i);
                List<TxtPoint> txtPoints = writePoint(geometryN, i + 1, bufferedWriter);
                txtPointAll.addAll(txtPoints);
            }

            bufferedWriter.write(String.format("%s,%s,%s,%s,面,%s,%s,%s,@", txtPointAll.size(), geometry.getArea(), "", "", "", "", ""));
            bufferedWriter.newLine();

            for (TxtPoint txtPoint : txtPointAll) {
                bufferedWriter.write(String.format("J%s,%s,%s,%s", txtPoint.getPointIndex(), txtPoint.getPolygonIndex(), txtPoint.getX(), txtPoint.getY()));
                bufferedWriter.newLine();
            }
        }
        return bufferedWriter;
    }

    /***
     * 自定义头写入到流
     * @param outputStream
     * @param geometry
     * @return
     */
    public BufferedWriter geometryToTxt(OutputStream outputStream, Geometry geometry) {
        try {
            BufferedWriter out = new BufferedWriter(new OutputStreamWriter(outputStream));
            BufferedWriter bufferedWriterRe = writeToStream(out, geometry);
            return bufferedWriterRe;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /***
     * 空间数据转 txt
     * @param filePath 文件路径
     * @param geometry 空间数据
     */
    public void geometryToTxt(String filePath, Geometry geometry) {
        geometryToTxt(filePath, geometry);
    }

    /**
     * 设置头
     *
     * @param bufferedWriter
     * @param geometry
     */
    private void setHead(BufferedWriter bufferedWriter, Geometry geometry,String tapeNumber) throws IOException {


        Coordinate[] coordinates = geometry.getBoundary().getCoordinates();
        double x = coordinates[0].getX();

        String number = String.valueOf(x);
        int precisionLength = number.split("\\.")[1].length();
        double precision = Math.pow(10, -precisionLength);

        bufferedWriter.write("格式版本号=");
        bufferedWriter.newLine();
        bufferedWriter.write("数据产生单位=");
        bufferedWriter.newLine();
        bufferedWriter.write(String.format("数据产生日期=%s", new SimpleDateFormat("yyyy-MM-dd").format(new Date())));
        bufferedWriter.newLine();
        bufferedWriter.write("坐标系=2000国家大地坐标系");
        bufferedWriter.newLine();

        String zoneTypeString = "3";
        bufferedWriter.write(String.format("几度分带=%s", zoneTypeString));
        bufferedWriter.newLine();

        String projectionTypeString = "高斯克吕格";
        bufferedWriter.write(String.format("投影类型=%s", projectionTypeString));
        bufferedWriter.newLine();
        bufferedWriter.write("计量单位=米");
        bufferedWriter.newLine();
        bufferedWriter.write(String.format("带号=%s", tapeNumber));
        bufferedWriter.newLine();
        bufferedWriter.write(String.format("精度=%s", precision));
        bufferedWriter.newLine();

        bufferedWriter.write("转换参数=0,0,0,0,0,0,0");
        bufferedWriter.newLine();


    }

    /***
     * 写入点
     * @param geometry
     * @param index
     * @param bufferedWriter
     * @throws IOException
     */
    private List<TxtPoint> writePoint(Geometry geometry, Integer index, BufferedWriter bufferedWriter) throws IOException {

        List<TxtPoint> points = new ArrayList<>();
        Coordinate[] coordinates = geometry.getCoordinates();
        for (int i = 0; i < coordinates.length; i++) {
            Coordinate coordinate = coordinates[i];
            int pointIndex = i + 1;
            if (pointIndex == coordinates.length) {
                pointIndex = 1;
            }
            TxtPoint point = TxtPoint.builder().pointIndex(pointIndex).polygonIndex(index)
                    .x(new BigDecimal(String.valueOf(coordinate.getX())))
                    .y(new BigDecimal(String.valueOf(coordinate.getY()))).build();
            points.add(point);
        }
        return points;

    }
}
